import asyncio

import aiohttp

from ..logger import log
from .utils import CapmonsterException, check_response


class Capmonster:
    __SOFT_ID = 30
    _HOST_URL = "https://api.capmonster.cloud"
    _CREATE_TASK_URL = "/createTask"
    _TASK_RESULT_URL = "/getTaskResult"
    _BALANCE_URL = "/getBalance"

    def __init__(self, client_key):
        self._client_key = client_key
        self.session = aiohttp.ClientSession()

    async def get_balance(self) -> float:
        data = {"clientKey": self._client_key}
        response = await self._make_request("getBalance", data)
        return response.get("balance")

    async def get_task_result(self, task_id: int) -> str | bool:
        data = {"clientKey": self._client_key, "taskId": task_id}
        result = await self._make_request("getTaskResult", data)
        is_ready = await self._is_ready(result)
        if is_ready:
            return result.get("solution")
        else:
            return False

    async def join_task_result(
        self, task_id: int, maximum_time: int = 120
    ) -> dict | bool:
        for i in range(0, maximum_time + 1, 2):
            result = await self.get_task_result(task_id)
            if result is not False and result is not None:
                return result
            elif result is False:
                i += 1
                await asyncio.sleep(2)
        raise CapmonsterException(
            61, "ERROR_MAXIMUM_TIME_EXCEED", "Maximum time is exceed."
        )

    async def join_task_result_async(
        self, task_id: int, maximum_time: int = 120
    ) -> str:
        for i in range(0, maximum_time + 1, 2):
            result = await self.get_task_result(task_id)
            if result is not False and result is not None:
                return result
            elif result is False:
                i += 1
                await asyncio.sleep(2)
        raise CapmonsterException(
            61, "ERROR_MAXIMUM_TIME_EXCEED", "Maximum time is exceed."
        )

    @staticmethod
    async def _is_ready(response: dict) -> bool:
        status = response.get("status")
        if status == "processing":
            return False
        elif status == "ready":
            return True

    @check_response()
    async def _make_request(self, method: str, data: dict) -> dict:
        _method = None
        if method == "getBalance":
            _method = self._BALANCE_URL
        elif method == "getTaskResult":
            _method = self._TASK_RESULT_URL
        elif method == "createTask":
            _method = self._CREATE_TASK_URL
            data["softId"] = self.__SOFT_ID
        try:
            async with await self.session.post(
                "{}{}".format(self._HOST_URL, _method), json=data
            ) as response:
                resp = await response.json(content_type=None)
                return resp
        except Exception as err:
            raise CapmonsterException(-1, type(err).__name__, str(err))


class Proxy(Capmonster):
    def __init__(self, client_key):
        super().__init__(client_key)
        self._proxy_type = None
        self._proxy_address = None
        self._proxy_port = None
        self._proxy_login = None
        self._proxy_password = None

    async def set_proxy(
        self,
        proxy_type: str,
        proxy_address: str,
        proxy_port: int,
        proxy_login: str = None,
        proxy_password: str = None,
    ):
        self._proxy_type = proxy_type
        self._proxy_address = proxy_address
        self._proxy_port = proxy_port
        self._proxy_login = proxy_login
        self._proxy_password = proxy_password

    async def disable_proxy(self):
        self._proxy_type = None
        self._proxy_address = None
        self._proxy_port = None
        self._proxy_login = None
        self._proxy_password = None

    async def _is_proxy_task(self, data: dict) -> tuple[dict, bool]:
        """
        Determine for is this a proxy task or not?
        e.g if you are not set the values with set_proxy method, it is a proxyless method, or if you are set up it is a
        proxy task.
        """
        if self._proxy_type and self._proxy_address and self._proxy_port:
            data["task"]["proxyType"] = self._proxy_type
            data["task"]["proxyAddress"] = self._proxy_address
            data["task"]["proxyPort"] = self._proxy_port
            if self._proxy_login and self._proxy_password:
                data["task"]["proxyLogin"] = self._proxy_login
                data["task"]["proxyPassword"] = self._proxy_password
            return data, True
        data["task"]["type"] += "Proxyless"
        return data, False
